#pragma once
/*
 * This class is a base class of CGenome_Index_TableQ and
 * provides functions to construct the Genome Index Table from genome.
 * (1) Build index array to find the maskable repeats and marked with flags
 * (2) Build index array again and skipped all maskable repeats index.
 */
#include "refInBinFile.h"
#include "Genome_Index.h"
#include "Index_Table.h"
#include "ShortReadUtil.h"
#include "ColorSpaceRead.h"
#include <algorithm>
#include <limits>
using namespace std;

// Use OpenMP is gcc version is later than 4.2
#ifdef __GNUC__
#ifdef __GNUC_PATCHLEVEL__
#define __GNUC_VERSION__ (__GNUC__ * 10000 \
                            + __GNUC_MINOR__ * 100 \
                            + __GNUC_PATCHLEVEL__)
#else
#define __GNUC_VERSION__ (__GNUC__ * 10000 \
                            + __GNUC_MINOR__ * 100)
# endif
#if __GNUC_VERSION__ >= 40200
#include <omp.h>
#endif
#else
#ifdef _MSC_VER
#if _MSC_VER > 1500
#include <omp.h>
#endif
#endif
#endif

#define PRINT_MASKED_REPERAT_FLAG(table, genomeIndex)\
do {\
     for(unsigned int j = 0; j <= table->uiNoOfShift; j++) {\
        char masked = table->pbaRepeatMaskedFlag->b(genomeIndex + j) ? 'Y' : 'N';\
        cout << masked << endl;\
     }\
} while(0)

class CGenome_Index_Table : public CIndex_Table, public CGenome_Index
{
public:
    CGenome_Index_Table(void);
    ~CGenome_Index_Table(void);
    int getGenomeNTdata(const char* genomeListfileName, string refFormat);
    bool make_index_table(unsigned int uiKmer_Length, unsigned int uiSeedId,
                          bool bMapReadInColors, bool makedMathRepeats);
    bool read_index_table(const char* indexFilePath, bool bPrintErrMsg);
    bool save_index_table(const char* indexFilePath, bool bPrintErrMsg);

    // Compare two substring in the genome using their hashKey. Return true is I1.key < I2.key
    bool compareKey(CIndex_Type I1, CIndex_Type I2);
    // Compare two substring in the genome using the alphabetical order. Return true is I1.order < I2.order
    bool compareSubstring(CIndex_Type I1, CIndex_Type I2, unsigned int slidingWindows);
    // Return true if the two substring in the genome are the same.
    bool sameSubstring(CIndex_Type I1, CIndex_Type I2, unsigned int slidingWindows);

    // Flag array which mark the index skipped because of mathematical repeats.
    CboolFlagArray* pbaRepeatMaskedFlag;
    inline bool isMasked(unsigned int GenomeIndex) const {
        return(this->pbaMaskedFlag->b(GenomeIndex));
    };
protected:
    string get_index_path(string index_path); // get default index path if the pass in is null.
    int chooseHashFunction(unsigned int uiReadLength, unsigned int \
                           fullSensitiveThreshold, bool bMapReadInColors);
private:
    int num_of_repeat_patterns;

    int initialization(void);
    // check the sliding windows with N and mask them
    int check_masked_flags(void);
    ////// add repeat masked regions from files. Those regions will be skipped from the index table.
    int add_repeat_masked_flags(void);  // Currently not used
    //// find mathematical repeats
    int find_maskable_mathmatical_repeats(unsigned int uiReadLength, unsigned int uiSubThreshold);
    // called by previous function that check the neighbor of index array
    int find_mathmatical_repeats(void);
    // called by previous function that check the neighbor of index array in a bucket.
    int find_mathmatical_repeats_in_a_bucket
    (ofstream& ofile, unsigned int BucketStart, unsigned int NextBucketStart);
    // Private function called Construct_IndexTable, which do the counting for each bucket - Step 1
    int countBucketSize(void);
    int countBucketSize4Chr(int chrId, unsigned int kmer_length);
    int bucketCount2Index(unsigned int uiNonMaskedLoci);
    // Hash the Kmer to the HashIndexTable bucket, without sorting the dHashkey
    int hashKmer2Bucket(void);
    int hashKmer2Bucket4Chr(int chrId, unsigned int kmer_length);
    // Sort the index table. If substringLength == 0, sorted with pre-defined hash function. Else sort with corresponding substring.
    int sortTable(unsigned int substringLength);
};

// functor to compare genome indices. If windowLength == 0, using the Seedkey, else compare 2 loci with the sliding windows
class CcompareFunctor4Sort
{
public:
    static const unsigned int MAX_SUBSTRING_LENGTH = 64;
    CGenome_Index_Table* pGenomeIndexTable;
    unsigned int windowLength;
    CcompareFunctor4Sort(CGenome_Index_Table* pGenomeIndexTable, unsigned int windowLength) {
        this->pGenomeIndexTable = pGenomeIndexTable;
        this->windowLength = windowLength;
        if (windowLength >= MAX_SUBSTRING_LENGTH) {
            cout << "Warning!, Excess the limit!" << endl;
        }
    }
    bool operator()(CIndex_Type I1, CIndex_Type I2) {
        if (windowLength == 0) {
            return(pGenomeIndexTable->compareKey(I1, I2));
        } else {
            return(pGenomeIndexTable->compareSubstring(I1, I2, windowLength));
        }
    }
};

bool testTable(CGenome_Index_Table& table);

